#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sem.h> // 信号量
#include <sys/ipc.h> // IPC
#include <sys/shm.h> // 共享内存
#include <fcntl.h>
typedef union semun
{
  int val;
  struct semid_ds *buf;
  unsigned short *array;
} semun;

void init_ops(struct sembuf *ops)
{
  ops[0].sem_num = 0;
  ops[0].sem_op = -1;
  ops[0].sem_flg = SEM_UNDO;

  ops[1].sem_num = 1;
  ops[1].sem_op = -1;
  ops[1].sem_flg = SEM_UNDO;

  ops[2].sem_num = 2;
  ops[2].sem_op = -1;
  ops[2].sem_flg = SEM_UNDO;

  ops[3].sem_num = 0;
  ops[3].sem_op = 1;
  ops[3].sem_flg = SEM_UNDO;

  ops[4].sem_num = 1;
  ops[4].sem_op = 1;
  ops[4].sem_flg = SEM_UNDO;

  ops[5].sem_num = 2;
  ops[5].sem_op = 1;
  ops[5].sem_flg = SEM_UNDO;
}

int main(int argc, char *argv[])
{

  key_t key_1, key_2;
  int shmid, semid, fd;
  char *ptr, *temp, buf[1024];
  int count = 0;

  // 获取key
  key_1 = ftok("/", 'a');
  key_2 = ftok("/", 'b');
  if (key_1 < 0 || key_2 < 0)
  {
    perror("获取key失败");
    exit(1);
  }
  // 获取共享内存id
  shmid = shmget(key_1, 1024, 0666 | IPC_CREAT);
  if (shmid < 0)
  {
    perror("获取共享内存失败");
    exit(1);
  }
  // 获取信号量id
  semid = semget(key_2, 3, 0666 | IPC_CREAT);
  if (semid < 0)
  {
    perror("获取信号量失败");
    exit(1);
  }

  // 初始化信号量
  // semun arg;
  // unsigned short *arr = {1, 0, 1}; // 信号量值
  // arg.array = arr;
  // semctl(semid, 0, SETALL, arg); // 设置信号量

  // 初始化信号量操作
  struct sembuf ops[6];
  init_ops(ops);

  // 获取共享内存地址
  ptr = shmat(shmid, NULL, 0);
  if (ptr == (char *)-1)
  {
    perror("获取共享内存地址失败");
    exit(1);
  }

  if (argc != 2)
  {
    printf("Usage: ./receive filename\n");
    exit(1);
  }

  // 打开文件
  fd = open(argv[1], O_WRONLY | O_CREAT); // 只读打开,不存在创建
  if (fd < 0)
  {
    perror("打开文件失败");
    exit(1);
  }

  // 从共享内存读数据
  while (1)
  {
    // 等待产品
    semop(semid, &ops[1], 1);
    // 等待锁
    semop(semid, &ops[0], 1);
    // 开始操作
    // 读共享内存数据
    count = 0; // 清空count
    temp = ptr;
    while (1)
    {
      if (*temp == '\0' || count >= 1024)
      {
        break;
      }
      count++;
      temp++;
    }
    printf("count:%d\n", count);
    memcpy(buf, ptr, count);
    // printf("buf:%s\n", buf);
    if (strcmp(buf, "end") == 0) // 判断是否为end
    {
      // 释放锁
      semop(semid, &ops[3], 1);
      // 空间数加1
      semop(semid, &ops[5], 1);
      break;
    }

    // 写入文件中
    write(fd, buf, count);

    // 清空ptr
    memset(ptr, '\0', 1024);
    // 清空buf
    memset(buf, '\0', sizeof(buf));

    // 释放锁
    semop(semid, &ops[3], 1);
    // 空间数加1
    semop(semid, &ops[5], 1);
  }

  close(fd);                     // 关闭文件
  shmdt(ptr);                    // 释放共享内存
  shmctl(shmid, IPC_RMID, NULL); // 删除共享内存
  semctl(semid, 0, IPC_RMID);    // 删除信号量
  printf("接收完成\n");
  return 0;
}